获取 Observable 当前值的简单方法(从 BehaviorSubject 获取)
Simple way to get current value of an Observable (Obtained from a BehaviorSubject)
这道题看起来有点长,不过应该是一道很基础的rxjs题。如果可以,请帮忙。
我正在编写一个 Angular 7 应用程序,其中包含如下服务、组件和模板,我想以一种简单的方式访问可观察对象的最后一个值。
SoundMeterService
在服务中,我有一个私有的 BehaviorSubject,我用它来使用 'next' 发出值。
我使用 .asObservable() 将此 BehaviorSubject 公开给消费者,以防止消费者执行 .next() 并发出新值。
在服务中,我使用 .value.
访问 listeningStatusSubject
的当前值
export type SoundMeterStatus = 'noResults' | 'isRunning' | 'hasResults';
export class SoundMeterService {
private listeningStatusSubject: BehaviorSubject<SoundMeterStatus> = new BehaviorSubject<SoundMeterStatus>('noResults');
get listeningStatus() { return this.listeningStatusSubject.asObservable(); }
// somewhere in the code I emit a different value
someFunc() {
if (this.listeningStatusSubject.value === 'noResults' )
this.listeningStatusSubject.next('isRunning');
}
}
SoundMeterComponent
遵循 Observable 的最佳实践,在组件中,我使用直接访问服务的 Observable(通过 listeningStatus
getter),以便在模板中自动订阅和取消订阅(见下文)。
export class SoundMeterComponent {
serviceListeningStatus$ = this.sms.listeningStatus;
constructor(
private sms: SoundMeterService
) {}
ngOnInit() {
// I want to check the value of listeningStatus in a simple way without pulling the value out and saving it locally in SoundMeterComponent
}
}
SoundMeterTemplate
我正在使用 Angular 的异步管道,这样它会在组件被销毁时自动订阅和取消订阅可观察对象(以避免提到的手动订阅 here)
<div *ngIf="(serviceListeningStatus | async) === 'noResults' ">
<span>some text</span>
</div>
我的问题
这种方法非常有效。但是我想在 ngOnInit() 中以一种简单的方式检查 listeningStatus
的值,而不是将值提取出来并将其本地保存在 SoundMeterComponent 中,并且不公开 BehaviorSubject listeningStatusSubject
本身以使用 .值,如下所述:
1) 从服务中提取值并将其保存在本地 SoundMeterComponent 中:
SoundMeterComponent
export class SoundMeterComponent {
serviceListeningStatus$ = this.sms.listeningStatus;
private serviceListeningStatus;
constructor(
private sms: SoundMeterService
) {}
ngOnInit() {
// Pulling the value from the service:
this.sms.listeningStatus.subscribe( status => this.serviceListeningStatus = status ); // I'm trying to avoid this
if (this.serviceListeningStatus === 'noResults' ) {
displayNoResultsMessage();
}
}
}
2) 公开 BehaviorSubject 本身并使用 .value 检查其值,如下所示:
SoundMeterService
export class SoundMeterService {
private listeningStatus: BehaviorSubject<SoundMeterStatus> = new BehaviorSubject<SoundMeterStatus>('noResults');
get listeningStatus() { return this.listeningStatus; }
}
SoundMeterComponent
export class SoundMeterComponent {
serviceListeningStatus = this.sms.listeningStatus;
constructor(
private sms: SoundMeterService
) {}
ngOnInit() {
if (this.sms.listeningStatus === 'noResults' ) {
displayNoResultsMessage();
}
}
}
SoundMeterTemplate
<div *ngIf="(serviceListeningStatus.asObservable() | async) === 'noResults' ">
<span>some text</span>
</div>
请帮助我了解哪种方法更好,同时保持代码简洁明了。我知道有很多关于如何从像 Simple way to get the current value of a BehaviorSubject with rxjs5 或 这样的 BehaviorSubject 获取值的问题如何获取 RxJS Subject 或 Observable 的当前值?
,但请注意,这是一个不同的问题。
谢谢!
始终可以将 async/await 与 ngOnInit 结合使用并将可观察对象转换为 Promise,以提高可读性:
async ngOnInit = () => {
const currentValue = await this.sms.listeningStatus.pipe(take(1)).toPromise();
// do stuff with currentValue here
}
(注意:在将热可观察对象转换为承诺时始终使用 take(1) - 否则它们将永远无法解决,因为您需要在承诺解决之前完成可观察对象)。
恐怕您必须订阅 this.sms.listeningStatus,这就是 observables 的工作原理。如果您想坚持使用真正的异步数据处理方法,则没有解决方法。
您唯一可以做的就是将订阅移动到其他地方(例如使用异步管道移动到模板)
据我所知,我首先会质疑 BehaviourSubject
的用法。
RxJs,尽管它很棒,但并不适合所有用例,您的可能就是其中之一。
.value
的用法表示该 imo。
在 SoundMeterService
中使用一个简单的 non Observable 变量来保持状态可能会解决你所有的问题。
如果您不想这样做,我的建议是尝试以下操作:
在您的流程中有几个地方您想检查 listeningStatus
和
您可以选择使用 tap 运算符并在一个地方对所有选项(不仅 'noResults')执行副作用?
示例:
export class SoundMeterComponent {
serviceListeningStatus$ = this.sms.listeningStatus.pipe(
tap((status) => {
if (value === 'noResults') {
displayNoResultsMessage();
}
if (value === 'xxx') {
doXXXStuff();
}
})
);
constructor(private sms: SoundMeterService) {}
ngOnInit() {}
}
然后模板订阅 serviceListeningStatus$
:
<div *ngIf="(serviceListeningStatus$ | async) === 'noResults' ">
<span>some text</span>
</div>
我认为你的问题对于想要做 RxJs 的应用程序来说是一个很好的例子,因为它很棒,但是由于 Angular 框架中缺乏对反应式方法的全面支持,你不能完全反应。我正在考虑 ngOnInit
或用户进行的点击事件。
如果你仍然想使用 RxJs,看看 ngx-template-stream or at this great proposal 可能会有所帮助。
干杯克里斯
这道题看起来有点长,不过应该是一道很基础的rxjs题。如果可以,请帮忙。
我正在编写一个 Angular 7 应用程序,其中包含如下服务、组件和模板,我想以一种简单的方式访问可观察对象的最后一个值。
SoundMeterService
在服务中,我有一个私有的 BehaviorSubject,我用它来使用 'next' 发出值。 我使用 .asObservable() 将此 BehaviorSubject 公开给消费者,以防止消费者执行 .next() 并发出新值。 在服务中,我使用 .value.
访问listeningStatusSubject
的当前值
export type SoundMeterStatus = 'noResults' | 'isRunning' | 'hasResults';
export class SoundMeterService {
private listeningStatusSubject: BehaviorSubject<SoundMeterStatus> = new BehaviorSubject<SoundMeterStatus>('noResults');
get listeningStatus() { return this.listeningStatusSubject.asObservable(); }
// somewhere in the code I emit a different value
someFunc() {
if (this.listeningStatusSubject.value === 'noResults' )
this.listeningStatusSubject.next('isRunning');
}
}
SoundMeterComponent
遵循 Observable 的最佳实践,在组件中,我使用直接访问服务的 Observable(通过 listeningStatus
getter),以便在模板中自动订阅和取消订阅(见下文)。
export class SoundMeterComponent {
serviceListeningStatus$ = this.sms.listeningStatus;
constructor(
private sms: SoundMeterService
) {}
ngOnInit() {
// I want to check the value of listeningStatus in a simple way without pulling the value out and saving it locally in SoundMeterComponent
}
}
SoundMeterTemplate
我正在使用 Angular 的异步管道,这样它会在组件被销毁时自动订阅和取消订阅可观察对象(以避免提到的手动订阅 here)
<div *ngIf="(serviceListeningStatus | async) === 'noResults' ">
<span>some text</span>
</div>
我的问题
这种方法非常有效。但是我想在 ngOnInit() 中以一种简单的方式检查 listeningStatus
的值,而不是将值提取出来并将其本地保存在 SoundMeterComponent 中,并且不公开 BehaviorSubject listeningStatusSubject
本身以使用 .值,如下所述:
1) 从服务中提取值并将其保存在本地 SoundMeterComponent 中:
SoundMeterComponent
export class SoundMeterComponent {
serviceListeningStatus$ = this.sms.listeningStatus;
private serviceListeningStatus;
constructor(
private sms: SoundMeterService
) {}
ngOnInit() {
// Pulling the value from the service:
this.sms.listeningStatus.subscribe( status => this.serviceListeningStatus = status ); // I'm trying to avoid this
if (this.serviceListeningStatus === 'noResults' ) {
displayNoResultsMessage();
}
}
}
2) 公开 BehaviorSubject 本身并使用 .value 检查其值,如下所示:
SoundMeterService
export class SoundMeterService {
private listeningStatus: BehaviorSubject<SoundMeterStatus> = new BehaviorSubject<SoundMeterStatus>('noResults');
get listeningStatus() { return this.listeningStatus; }
}
SoundMeterComponent
export class SoundMeterComponent {
serviceListeningStatus = this.sms.listeningStatus;
constructor(
private sms: SoundMeterService
) {}
ngOnInit() {
if (this.sms.listeningStatus === 'noResults' ) {
displayNoResultsMessage();
}
}
}
SoundMeterTemplate
<div *ngIf="(serviceListeningStatus.asObservable() | async) === 'noResults' ">
<span>some text</span>
</div>
请帮助我了解哪种方法更好,同时保持代码简洁明了。我知道有很多关于如何从像 Simple way to get the current value of a BehaviorSubject with rxjs5 或 这样的 BehaviorSubject 获取值的问题如何获取 RxJS Subject 或 Observable 的当前值? ,但请注意,这是一个不同的问题。 谢谢!
始终可以将 async/await 与 ngOnInit 结合使用并将可观察对象转换为 Promise,以提高可读性:
async ngOnInit = () => {
const currentValue = await this.sms.listeningStatus.pipe(take(1)).toPromise();
// do stuff with currentValue here
}
(注意:在将热可观察对象转换为承诺时始终使用 take(1) - 否则它们将永远无法解决,因为您需要在承诺解决之前完成可观察对象)。
恐怕您必须订阅 this.sms.listeningStatus,这就是 observables 的工作原理。如果您想坚持使用真正的异步数据处理方法,则没有解决方法。 您唯一可以做的就是将订阅移动到其他地方(例如使用异步管道移动到模板)
据我所知,我首先会质疑 BehaviourSubject
的用法。
RxJs,尽管它很棒,但并不适合所有用例,您的可能就是其中之一。
.value
的用法表示该 imo。
在 SoundMeterService
中使用一个简单的 non Observable 变量来保持状态可能会解决你所有的问题。
如果您不想这样做,我的建议是尝试以下操作:
在您的流程中有几个地方您想检查 listeningStatus
和
您可以选择使用 tap 运算符并在一个地方对所有选项(不仅 'noResults')执行副作用?
示例:
export class SoundMeterComponent {
serviceListeningStatus$ = this.sms.listeningStatus.pipe(
tap((status) => {
if (value === 'noResults') {
displayNoResultsMessage();
}
if (value === 'xxx') {
doXXXStuff();
}
})
);
constructor(private sms: SoundMeterService) {}
ngOnInit() {}
}
然后模板订阅 serviceListeningStatus$
:
<div *ngIf="(serviceListeningStatus$ | async) === 'noResults' ">
<span>some text</span>
</div>
我认为你的问题对于想要做 RxJs 的应用程序来说是一个很好的例子,因为它很棒,但是由于 Angular 框架中缺乏对反应式方法的全面支持,你不能完全反应。我正在考虑 ngOnInit
或用户进行的点击事件。
如果你仍然想使用 RxJs,看看 ngx-template-stream or at this great proposal 可能会有所帮助。
干杯克里斯